﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

// Форма параметризуется.
//
// Параметры:
//     НаборСсылок - Массив, СписокЗначений - набор элементов для анализа.
//                                            Может быть коллекцией элементов, обладающих полем "Ссылка".
//

#Область ОбработчикиСобытийФормы

&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
	
	Если Не Параметры.ОткрытаПоСценарию Тогда
		ВызватьИсключение НСтр("ru = 'Обработка не предназначена для непосредственного использования.'");
	КонецЕсли;
	
	ИнициализироватьЗаменяемыеСсылки(МассивСсылокИзСписка(Параметры.НаборСсылок));
	
	ЕстьПравоБезвозвратногоУдаления = ПравоДоступа("АдминистрированиеДанных", Метаданные);
	СобытиеОповещенияОЗамене        = Обработки.ЗаменаИОбъединениеЭлементов.СобытиеОповещенияОЗамене();
	ТекущийВариантУдаления          = "Пометка";
	
	// Инициализируем динамический список на форме - имитация формы выбора.
	ОсновныеМетаданные = ЦелевойЭлемент.Метаданные();
	Список.ПроизвольныйЗапрос = Ложь;
	
	ПараметрыДинамическогоСписка = ОбщегоНазначения.СтруктураСвойствДинамическогоСписка();
	ПараметрыДинамическогоСписка.ОсновнаяТаблица = ОсновныеМетаданные.ПолноеИмя();
	ПараметрыДинамическогоСписка.ДинамическоеСчитываниеДанных = Истина;
	ОбщегоНазначения.УстановитьСвойстваДинамическогоСписка(Элементы.Список, ПараметрыДинамическогоСписка);
	
	Элементы.Список.ИзменятьПорядокСтрок = Ложь;
	Элементы.Список.ИзменятьСоставСтрок  = Ложь;
	
	СписокЗаменяемых = Новый СписокЗначений;
	СписокЗаменяемых.ЗагрузитьЗначения(ЗаменяемыеСсылки.Выгрузить().ВыгрузитьКолонку("Ссылка"));
	ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Ссылка", СписокЗаменяемых,
		ВидСравненияКомпоновкиДанных.НеВСписке, НСтр("ru = 'Не показывать заменяемые'"), Истина, 
		РежимОтображенияЭлементаНастройкиКомпоновкиДанных.Недоступный, "5bf5cd06-c1fd-4bd3-94b9-4e9803e90fd5");
	Если ОбщийВладелецЗаменяемыхСсылок <> Неопределено Тогда 
		ОбщегоНазначенияКлиентСервер.УстановитьЭлементОтбораДинамическогоСписка(Список, "Владелец", ОбщийВладелецЗаменяемыхСсылок);
		Элементы.ПодсказкаОтбораСписка.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'В списке показаны подходящие варианты с отбором: %2 = %3.'"),
			ОбщегоНазначения.ПредставлениеСписка(ОсновныеМетаданные),
			ПредставлениеВладельца(ОсновныеМетаданные),
			ОбщегоНазначения.ПредметСтрокой(ОбщийВладелецЗаменяемыхСсылок));
	Иначе
		Элементы.ПодсказкаОтбораСписка.Видимость = Ложь;
	КонецЕсли;
	
	Если ЗаменяемыеСсылки.Количество() > 1 Тогда
		Элементы.НадписьТипВыбираемого.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Выберите один из элементов ""%1"", на который следует заменить выбранные значения (%2):'"),
			ОсновныеМетаданные.Представление(), ЗаменяемыеСсылки.Количество());
	Иначе
		Заголовок = НСтр("ru = 'Замена элемента'");
		Элементы.НадписьТипВыбираемого.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Выберите один из элементов ""%1"", на который следует заменить ""%2"":'"),
			ОсновныеМетаданные.Представление(), ЗаменяемыеСсылки[0].Ссылка);
	КонецЕсли;
	Элементы.ПодсказкаВыбораЦелевогоЭлемента.Заголовок = НСтр("ru = 'Элемент для замены не выбран.'");
	
	// Инициализация шагов пошагового мастера.
	НастройкиМастера = НастройкиПошаговогоМастера(Элементы);
	
	// 1. Выбор основного элемента.
	ШагВыбор = ДобавитьШагМастера(Элементы.ШагВыборЦелевогоЭлемента);
	ШагВыбор.КнопкаНазад.Видимость = Ложь;
	ШагВыбор.КнопкаДалее.Заголовок = НСтр("ru = 'Заменить >'");
	ШагВыбор.КнопкаДалее.Подсказка = НСтр("ru = 'Начать замену элементов'");
	ШагВыбор.КнопкаОтмена.Заголовок = НСтр("ru = 'Отмена'");
	ШагВыбор.КнопкаОтмена.Подсказка = НСтр("ru = 'Отказаться от замены элементов'");
	
	// 2. Ожидание процесса.
	Шаг = ДобавитьШагМастера(Элементы.ШагЗамена);
	Шаг.КнопкаОтмена.Видимость = Ложь;
	Шаг.КнопкаДалее.Видимость = Ложь;
	Шаг.КнопкаНазад.Заголовок = НСтр("ru = 'Прервать'");
	Шаг.КнопкаНазад.Подсказка = НСтр("ru = 'Вернуться к выбору основного элемента'");
	
	// 3. Проблемы замены ссылок.
	Шаг = ДобавитьШагМастера(Элементы.ШагПовторЗамены);
	Шаг.КнопкаНазад.Заголовок = НСтр("ru = '< Назад'");
	Шаг.КнопкаНазад.Подсказка = НСтр("ru = 'Вернутся к выбору целевого элемента'");
	Шаг.КнопкаДалее.Заголовок = НСтр("ru = 'Повторить замену >'");
	Шаг.КнопкаДалее.Подсказка = НСтр("ru = 'Повторить замену элементов'");
	Шаг.КнопкаОтмена.Заголовок = НСтр("ru = 'Закрыть'");
	Шаг.КнопкаОтмена.Подсказка = НСтр("ru = 'Закрыть результаты замены элементов'");
	
	// 4. Ошибки выполнения.
	Шаг = ДобавитьШагМастера(Элементы.ШагВозниклаОшибка);
	Шаг.КнопкаНазад.Видимость = Ложь;
	Шаг.КнопкаДалее.Видимость = Ложь;
	Шаг.КнопкаОтмена.Заголовок = НСтр("ru = 'Закрыть'");
	
	// Обновление элементов формы.
	НастройкиМастера.ТекущийШаг = ШагВыбор;
	ВидимостьДоступность(ЭтотОбъект);
	
КонецПроцедуры

&НаКлиенте
Процедура ПриОткрытии(Отказ)
	ПриАктивацииШагаМастера();
КонецПроцедуры

&НаКлиенте
Процедура ПередЗакрытием(Отказ, ЗавершениеРаботы, ТекстПредупреждения, СтандартнаяОбработка)
	
	Если Элементы.ШагиМастера.ТекущаяСтраница <> Элементы.ШагЗамена
		Или Не НастройкиМастера.ПоказатьДиалогПередЗакрытием Тогда
		Возврат;
	КонецЕсли;
	
	Отказ = Истина;
	Если ЗавершениеРаботы Тогда
		Возврат;
	КонецЕсли;
	
	ТекстВопроса = НСтр("ru = 'Прервать замену элементов и закрыть форму?'");
	
	Кнопки = Новый СписокЗначений;
	Кнопки.Добавить(КодВозвратаДиалога.Прервать, НСтр("ru = 'Прервать'"));
	Кнопки.Добавить(КодВозвратаДиалога.Нет,      НСтр("ru = 'Не прерывать'"));
	
	Обработчик = Новый ОписаниеОповещения("ПослеПодтвержденияОтменыЗадания", ЭтотОбъект);
	ПоказатьВопрос(Обработчик, ТекстВопроса, Кнопки, , КодВозвратаДиалога.Нет);
	
КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// ЭЛЕМЕНТЫ

#Область ОбработчикиСобытийЭлементовШапкиФормы

&НаКлиенте
Процедура ПодсказкаВыбораЦелевогоЭлементаОбработкаНавигационнойСсылки(Элемент, Ссылка, СтандартнаяОбработка)
	
	СтандартнаяОбработка = Ложь;
	
	Если Ссылка = "ПереключениеРежимаУдаления" Тогда
		Если ТекущийВариантУдаления = "Непосредственно" Тогда
			ТекущийВариантУдаления = "Пометка" 
		Иначе
			ТекущийВариантУдаления = "Непосредственно" 
		КонецЕсли;
		
		СформироватьЦелевойЭлементИПодсказку(ЭтотОбъект);
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура СсылкаПодробнееНажатие(Элемент)
	СтандартныеПодсистемыКлиент.ПоказатьПодробнуюИнформацию(Неопределено, Элемент.Подсказка);
КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// ТАБЛИЦА Список

#Область ОбработчикиСобытийЭлементовТаблицыФормыСписок

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
	
	ПодключитьОбработчикОжидания("СформироватьЦелевойЭлементИПодсказкуОтложенно", 0.01, Истина);
	
КонецПроцедуры

&НаКлиенте
Процедура СписокВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	ШагВыборЦелевогоЭлементаПриНажатииКнопкиДалее();
КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// ТАБЛИЦА НеудачныеЗамены

#Область ОбработчикиСобытийЭлементовТаблицыФормыНеудачныеЗамены

&НаКлиенте
Процедура НеудачныеЗаменыПриАктивизацииСтроки(Элемент)
	ТекущиеДанные = Элемент.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		РасшифровкаПричиныНеудачи = "";
	Иначе
		РасшифровкаПричиныНеудачи = ТекущиеДанные.ПодробнаяПричина;
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура НеудачныеЗаменыВыбор(Элемент, ВыбраннаяСтрока, Поле, СтандартнаяОбработка)
	СтандартнаяОбработка = Ложь;
	
	Ссылка = НеудачныеЗамены.НайтиПоИдентификатору(ВыбраннаяСтрока).Ссылка;
	Если Ссылка <> Неопределено Тогда
		ПоказатьЗначение(, Ссылка);
	КонецЕсли;

КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// КОМАНДЫ

#Область ОбработчикиКомандФормы

&НаКлиенте
Процедура ОбработчикКнопкиМастера(Команда)
	
	Если Команда.Имя = НастройкиМастера.КнопкаДалее Тогда
		
		ШагМастераДалее();
		
	ИначеЕсли Команда.Имя = НастройкиМастера.КнопкаНазад Тогда
		
		ШагМастераНазад();
		
	ИначеЕсли Команда.Имя = НастройкиМастера.КнопкаОтмена Тогда
		
		ШагМастераОтмена();
		
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьЭлементНеудачнойЗамены(Команда)
	ТекущиеДанные = Элементы.НеудачныеЗамены.ТекущиеДанные;
	Если ТекущиеДанные <> Неопределено Тогда
		ПоказатьЗначение(, ТекущиеДанные.Ссылка);
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура РазвернутьВсеНеудачныеЗамены(Команда)
	ДеревоФормы = Элементы.НеудачныеЗамены;
	Для Каждого Элемент Из НеудачныеЗамены.ПолучитьЭлементы() Цикл
		ДеревоФормы.Развернуть(Элемент.ПолучитьИдентификатор(), Истина);
	КонецЦикла;
КонецПроцедуры

&НаКлиенте
Процедура СвернутьВсеНеудачныеЗамены(Команда)
	ДеревоФормы = Элементы.НеудачныеЗамены;
	Для Каждого Элемент Из НеудачныеЗамены.ПолучитьЭлементы() Цикл
		ДеревоФормы.Свернуть(Элемент.ПолучитьИдентификатор());
	КонецЦикла;
КонецПроцедуры

#КонецОбласти

////////////////////////////////////////////////////////////////////////////////
// ВСПОМОГАТЕЛЬНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ

#Область СлужебныеПроцедурыИФункции

////////////////////////////////////////////////////////////////////////////////
// Программный интерфейс мастера

// Инициализирует структуры мастера.
// 
// Параметры:
//   Элементы - ЭлементыФормы 
// 
// Возвращаемое значение:
//   Структура - описание настроек мастера.
//     Общедоступные настройки мастера:
//       * Шаги - Массив - описание шагов мастера. Только для чтения.
//           Для добавления шагов следует использовать функцию ДобавитьШагМастера.
//       * ТекущийШаг - см. ДобавитьШагМастера.
//       * ПоказатьДиалогПередЗакрытием - Булево - если Истина, то перед закрытием формы будет показано предупреждение.
//           Для изменения.
//     Служебные настройки мастера:
//       * ГруппаСтраниц - Строка - имя элемента формы, переданного в параметре ГруппаСтраниц.
//       * КнопкаДалее - Строка - имя элемента формы, переданного в параметре КнопкаДалее.
//       * КнопкаНазад - Строка - имя элемента формы, переданного в параметре КнопкаНазад.
//       * КнопкаОтмена - Строка - имя элемента формы, переданного в параметре КнопкаОтмена.
//
&НаСервереБезКонтекста
Функция НастройкиПошаговогоМастера(Элементы)
	НастройкиМастера = Новый Структура;
	НастройкиМастера.Вставить("Шаги", Новый Массив);
	НастройкиМастера.Вставить("ТекущийШаг", Неопределено);
	
	// Идентификаторы частей интерфейса.
	НастройкиМастера.Вставить("ГруппаСтраниц", Элементы.ШагиМастера.Имя);
	НастройкиМастера.Вставить("КнопкаДалее",   Элементы.ШагМастераДалее.Имя);
	НастройкиМастера.Вставить("КнопкаНазад",   Элементы.ШагМастераНазад.Имя);
	НастройкиМастера.Вставить("КнопкаОтмена",  Элементы.ШагМастераОтмена.Имя);
	
	// Для обработки длительных операций.
	НастройкиМастера.Вставить("ПоказатьДиалогПередЗакрытием", Ложь);
	
	// По умолчанию все отключено.
	Элементы.ШагМастераДалее.Видимость  = Ложь;
	Элементы.ШагМастераНазад.Видимость  = Ложь;
	Элементы.ШагМастераОтмена.Видимость = Ложь;
	
	Возврат НастройкиМастера;
КонецФункции

// Добавляет шаг мастера. Переходы между страницами будут происходить согласно порядку добавления.
//
// Параметры:
//   Страница - ГруппаФормы - страница, содержащая элементы шага.
//
// Возвращаемое значение:
//   Структура - описание настроек страницы, где:
//       * ИмяСтраницы - Строка - имя страницы.
//       * КнопкаДалее - Структура - описание кнопки "Далее", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию - "Далее >".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию - Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Истина.
//       * КнопкаНазад - Структура - описание кнопки "Назад", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию - "< Назад".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию - Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Ложь.
//       * КнопкаОтмена - Структура - описание кнопки "Отмена", где:
//           ** Заголовок - Строка - заголовок кнопки. По умолчанию - "Отмена".
//           ** Подсказка - Строка - подсказка для кнопки. По умолчанию соответствует заголовку кнопки.
//           ** Видимость - Булево - когда Истина то кнопка видна. По умолчанию - Истина.
//           ** Доступность - Булево - когда Истина то кнопку можно нажимать. По умолчанию - Истина.
//           ** КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. По умолчанию - Ложь.
//
&НаСервере
Функция ДобавитьШагМастера(Знач Страница)
	ОписаниеШага = Новый Структура;
	ОписаниеШага.Вставить("Индекс", 0);
	ОписаниеШага.Вставить("ИмяСтраницы", "");
	ОписаниеШага.Вставить("КнопкаНазад", КнопкаМастера());
	ОписаниеШага.Вставить("КнопкаДалее", КнопкаМастера());
	ОписаниеШага.Вставить("КнопкаОтмена", КнопкаМастера());
	 
	ОписаниеШага.ИмяСтраницы = Страница.Имя;
	
	ОписаниеШага.КнопкаНазад.Заголовок = НСтр("ru = '< Назад'");
	
	ОписаниеШага.КнопкаДалее.КнопкаПоУмолчанию = Истина;
	ОписаниеШага.КнопкаДалее.Заголовок = НСтр("ru = 'Далее >'");
	
	ОписаниеШага.КнопкаОтмена.Заголовок = НСтр("ru = 'Отмена'");
	
	НастройкиМастера.Шаги.Добавить(ОписаниеШага);
	
	ОписаниеШага.Индекс = НастройкиМастера.Шаги.ВГраница();
	Возврат ОписаниеШага;
КонецФункции

// Обновляет видимость и доступность элементов формы в соответствии с текущим шагом мастера.
&НаКлиентеНаСервереБезКонтекста
Процедура ВидимостьДоступность(Форма)
	
	Элементы = Форма.Элементы;
	НастройкиМастера = Форма.НастройкиМастера;
	ТекущийШаг = НастройкиМастера.ТекущийШаг;
	
	// Переключение страницы.
	Элементы[НастройкиМастера.ГруппаСтраниц].ТекущаяСтраница = Элементы[ТекущийШаг.ИмяСтраницы];
	
	// Обновление кнопок.
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаДалее],  ТекущийШаг.КнопкаДалее);
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаНазад],  ТекущийШаг.КнопкаНазад);
	ОбновитьСвойстваКнопкиМастера(Элементы[НастройкиМастера.КнопкаОтмена], ТекущийШаг.КнопкаОтмена);
	
КонецПроцедуры

// Выполняет переход мастера на указанную страницу.
//
// Параметры:
//   ШагИлиИндексИлиГруппаФормы - Структура
//                              - Число
//                              - ГруппаФормы - страницу, на которую необходимо перейти.
//
&НаКлиенте
Процедура ПерейтиНаШагМастера(Знач ШагИлиИндексИлиГруппаФормы)
	
	// Поиск шага.
	Тип = ТипЗнч(ШагИлиИндексИлиГруппаФормы);
	Если Тип = Тип("Структура") Тогда
		ОписаниеШага = ШагИлиИндексИлиГруппаФормы;
	ИначеЕсли Тип = Тип("Число") Тогда
		ИндексШага = ШагИлиИндексИлиГруппаФормы;
		Если ИндексШага < 0 Тогда
			ВызватьИсключение НСтр("ru = 'Попытка выхода назад из первого шага мастера'");
		ИначеЕсли ИндексШага > НастройкиМастера.Шаги.ВГраница() Тогда
			ВызватьИсключение НСтр("ru = 'Попытка выхода за последний шаг мастера'");
		КонецЕсли;
		ОписаниеШага = НастройкиМастера.Шаги[ИндексШага];
	Иначе
		ШагНайден = Ложь;
		ИмяИскомойСтраницы = ШагИлиИндексИлиГруппаФормы.Имя;
		Для Каждого ОписаниеШага Из НастройкиМастера.Шаги Цикл
			Если ОписаниеШага.ИмяСтраницы = ИмяИскомойСтраницы Тогда
				ШагНайден = Истина;
				Прервать;
			КонецЕсли;
		КонецЦикла;
		Если Не ШагНайден Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не найден шаг ""%1"".'"),
				ИмяИскомойСтраницы);
		КонецЕсли;
	КонецЕсли;
	
	// Переключение шага.
	НастройкиМастера.ТекущийШаг = ОписаниеШага;
	
	// Обновление видимости.
	ВидимостьДоступность(ЭтотОбъект);
	ПриАктивацииШагаМастера();
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// События мастера

&НаКлиенте
Процедура ПриАктивацииШагаМастера()
	
	ТекущаяСтраница = Элементы.ШагиМастера.ТекущаяСтраница;
	
	Если ТекущаяСтраница = Элементы.ШагВыборЦелевогоЭлемента Тогда
		
		СформироватьЦелевойЭлементИПодсказку(ЭтотОбъект);
		
	ИначеЕсли ТекущаяСтраница = Элементы.ШагЗамена Тогда
		
		НастройкиМастера.ПоказатьДиалогПередЗакрытием = Истина;
		ЦелевойЭлементРезультат = ЦелевойЭлемент; // Сохранение параметров запуска.
		ЗапуститьФоновоеЗаданиеКлиент();
		
	ИначеЕсли ТекущаяСтраница = Элементы.ШагПовторЗамены Тогда
		
		// Обновление количества неудач.
		Неудачные = Новый Соответствие;
		Для Каждого Строка Из НеудачныеЗамены.ПолучитьЭлементы() Цикл
			Неудачные.Вставить(Строка.Ссылка, Истина);
		КонецЦикла;
		
		КоличествоЗамен = ЗаменяемыеСсылки.Количество();
		Элементы.РезультатНеудачныеЗамены.Заголовок = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось заменить элементы (%1 из %2). В некоторых местах использования не может быть произведена
			           |автоматическая замена на ""%3""'"),
			Неудачные.Количество(),
			КоличествоЗамен,
			ЦелевойЭлемент);
		
		// Формирование списка успешных замен и чистка списка заменяемых.
		СписокОбновленного = Новый Массив;
		СписокОбновленного.Добавить(ЦелевойЭлемент);
		Для Номер = 1 По КоличествоЗамен Цикл
			ОбратныйИндекс = КоличествоЗамен - Номер;
			Ссылка = ЗаменяемыеСсылки[ОбратныйИндекс].Ссылка;
			Если Ссылка <> ЦелевойЭлемент И Неудачные[Ссылка] = Неопределено Тогда
				ЗаменяемыеСсылки.Удалить(ОбратныйИндекс);
				СписокОбновленного.Добавить(Ссылка);
			КонецЕсли;
		КонецЦикла;
		
		// Оповещение о выполненных заменах.
		ОповеститьОбУспешнойЗамене(СписокОбновленного);
		
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ШагМастераДалее()
	
	ТекущаяСтраница = Элементы.ШагиМастера.ТекущаяСтраница;
	Если ТекущаяСтраница = Элементы.ШагВыборЦелевогоЭлемента Тогда
		ШагВыборЦелевогоЭлементаПриНажатииКнопкиДалее();
	ИначеЕсли ТекущаяСтраница = Элементы.ШагПовторЗамены Тогда
		ПерейтиНаШагМастера(Элементы.ШагЗамена);
	Иначе
		ТекущийШаг = НастройкиМастера.ТекущийШаг; // см. ДобавитьШагМастера
		ПерейтиНаШагМастера(ТекущийШаг.Индекс + 1);
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ШагМастераНазад()
	
	ТекущаяСтраница = Элементы.ШагиМастера.ТекущаяСтраница;
	Если ТекущаяСтраница = Элементы.ШагПовторЗамены Тогда
		ПерейтиНаШагМастера(Элементы.ШагВыборЦелевогоЭлемента);
	Иначе
		Шаг = НастройкиМастера.ТекущийШаг; // см. ДобавитьШагМастера
		ПерейтиНаШагМастера(Шаг.Индекс - 1);
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ШагМастераОтмена()
	
	ТекущаяСтраница = Элементы.ШагиМастера.ТекущаяСтраница;
	Если ТекущаяСтраница = Элементы.ШагЗамена Тогда
		НастройкиМастера.ПоказатьДиалогПередЗакрытием = Ложь;
	КонецЕсли;
	
	Если Открыта() Тогда
		Закрыть();
	КонецЕсли;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Служебные процедуры замены и объединения элементов

&НаКлиенте
Процедура ШагВыборЦелевогоЭлементаПриНажатииКнопкиДалее()
	
	ТекущиеДанные = Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Тогда
		Возврат;
	ИначеЕсли ЗаменяемыеСсылки.Количество() = 1 И ТекущиеДанные.Ссылка = ЗаменяемыеСсылки.Получить(0).Ссылка Тогда
		ПоказатьПредупреждение(, НСтр("ru = 'Нельзя заменять элемент сам на себя.'"));
		Возврат;
	ИначеЕсли ЗначениеРеквизита(ТекущиеДанные, "ЭтоГруппа", Ложь) Тогда
		ПоказатьПредупреждение(, НСтр("ru = 'Нельзя заменять элемент на группу.'"));
		Возврат;
	КонецЕсли;
	
	ТекущийВладелец = ЗначениеРеквизита(ТекущиеДанные, "Владелец");
	Если ТекущийВладелец <> ОбщийВладелецЗаменяемыхСсылок Тогда
		Текст = НСтр("ru = 'Нельзя заменять на элемент, подчиненный другому владельцу.
			|У выбранного элемента владелец ""%1"", а у заменяемого - ""%2"".'");
		ПоказатьПредупреждение(, СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(Текст, ТекущийВладелец, ОбщийВладелецЗаменяемыхСсылок));
		Возврат;
	КонецЕсли;
	
	Если ЗначениеРеквизита(ТекущиеДанные, "ПометкаУдаления", Ложь) Тогда
		// Попытка заменить на элемент, помеченный на удаление.
		Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Элемент %1 помечен на удаление. Продолжить?'"),
			ТекущиеДанные.Ссылка);
		Описание = Новый ОписаниеОповещения("ПодтверждениеВыбораЦелевогоЭлемента", ЭтотОбъект);
		ПоказатьВопрос(Описание, Текст, РежимДиалогаВопрос.ДаНет);
	Иначе
		// Нужна дополнительная проверка по прикладным данным.
		ПроверкаДопустимостиЗаменыПрикладнойОбласти();
	КонецЕсли;
	
КонецПроцедуры

&НаКлиенте
Процедура ОповеститьОбУспешнойЗамене(Знач СписокДанных)
	ОбщегоНазначенияКлиент.ОповеститьОбИзмененииОбъектов(СписокДанных);
КонецПроцедуры

&НаКлиенте
Процедура СформироватьЦелевойЭлементИПодсказкуОтложенно()
	СформироватьЦелевойЭлементИПодсказку(ЭтотОбъект);
КонецПроцедуры

&НаКлиенте
Процедура ПодтверждениеВыбораЦелевогоЭлемента(Знач РезультатВопроса, Знач ДополнительныеПараметры) Экспорт
	Если РезультатВопроса <> КодВозвратаДиалога.Да Тогда
		Возврат;
	КонецЕсли;
	
	// Дополнительная проверка по прикладным данным.
	ПроверкаДопустимостиЗаменыПрикладнойОбласти();
КонецПроцедуры

&НаКлиенте
Процедура ПроверкаДопустимостиЗаменыПрикладнойОбласти()
	// Проверка допустимости замен с прикладной точки зрения.
	ТекстОшибки = ПроверитьВозможностьЗаменыСсылок();
	Если Не ПустаяСтрока(ТекстОшибки) Тогда
		НастройкиДиалога = Новый Структура;
		НастройкиДиалога.Вставить("ПредлагатьБольшеНеЗадаватьЭтотВопрос", Ложь);
		НастройкиДиалога.Вставить("Картинка", БиблиотекаКартинок.Предупреждение32);
		НастройкиДиалога.Вставить("КнопкаПоУмолчанию", 0);
		НастройкиДиалога.Вставить("Заголовок", НСтр("ru = 'Невозможно заменить элементы'"));
		
		Кнопки = Новый СписокЗначений;
		Кнопки.Добавить(0, НСтр("ru = 'ОК'"));
		
		СтандартныеПодсистемыКлиент.ПоказатьВопросПользователю(Неопределено, ТекстОшибки, Кнопки, НастройкиДиалога);
		Возврат;
	КонецЕсли;
	
	Шаг = НастройкиМастера.ТекущийШаг;// см. ДобавитьШагМастера
	ПерейтиНаШагМастера(Шаг.Индекс + 1);
КонецПроцедуры

&НаКлиенте
Процедура СформироватьЦелевойЭлементИПодсказку(Контекст)
	
	ТекущиеДанные = Контекст.Элементы.Список.ТекущиеДанные;
	Если ТекущиеДанные = Неопределено Или ЗначениеРеквизита(ТекущиеДанные, "ЭтоГруппа", Ложь) Тогда
		Возврат;
	КонецЕсли;
	Контекст.ЦелевойЭлемент = ТекущиеДанные.Ссылка;
	
	Количество = Контекст.ЗаменяемыеСсылки.Количество();
	Если Количество = 1 Тогда
		
		Если Контекст.ЕстьПравоБезвозвратногоУдаления Тогда
			Если Контекст.ТекущийВариантУдаления = "Пометка" Тогда
				ТекстПодсказки = НСтр("ru = 'Выбранный элемент будет заменен на ""[ЦелевойЭлемент]""
					|и <a href = ""[Гиперссылка]"">помечен на удаление</a>.'");
			Иначе
				ТекстПодсказки = НСтр("ru = 'Выбранный элемент будет заменен на ""[ЦелевойЭлемент]""
					|и <a href = ""[Гиперссылка]"">удален безвозвратно</a>.'");
			КонецЕсли;
		Иначе
			ТекстПодсказки = НСтр("ru = 'Выбранный элемент будет заменен на ""[ЦелевойЭлемент]""
				|и помечен на удаление.'");
		КонецЕсли;
		
		ПараметрыСтроки = Новый Структура();
		ПараметрыСтроки.Вставить("ЦелевойЭлемент", Контекст.ЦелевойЭлемент);
		ПараметрыСтроки.Вставить("Гиперссылка", "ПереключениеРежимаУдаления");
		
		ТекстПодсказки = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(ТекстПодсказки, ПараметрыСтроки);
		Контекст.Элементы.ПодсказкаВыбораЦелевогоЭлемента.Заголовок = СтроковыеФункцииКлиент.ФорматированнаяСтрока(ТекстПодсказки);
		
	Иначе
		
		Если Контекст.ЕстьПравоБезвозвратногоУдаления Тогда
			Если Контекст.ТекущийВариантУдаления = "Пометка" Тогда
				ТекстПодсказки = НСтр("ru = 'Выбранные элементы (%1) будут заменены на ""%2""
					|и <a href = ""[Действие]"">помечены на удаление</a>.'");
				ПараметрыСтроки = Новый Структура("Действие", "ПереключениеРежимаУдаления");
				ТекстПодсказки = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(ТекстПодсказки, ПараметрыСтроки);
			Иначе
				ТекстПодсказки = НСтр("ru = 'Выбранные элементы (%1) будут заменены на ""%2""
					|и <a href = ""[Действие]"">удалены безвозвратно</a>.'");
				ПараметрыСтроки = Новый Структура("Действие", "ПереключениеРежимаУдаления");
				ТекстПодсказки = СтроковыеФункцииКлиентСервер.ВставитьПараметрыВСтроку(ТекстПодсказки, ПараметрыСтроки);
			КонецЕсли;
		Иначе
			ТекстПодсказки = НСтр("ru = 'Выбранные элементы (%1) будут заменены на ""%2""
				|и помечен на удаление.'");
		КонецЕсли;
			
		ТекстПодсказки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ТекстПодсказки, Количество, Контекст.ЦелевойЭлемент);
		Контекст.Элементы.ПодсказкаВыбораЦелевогоЭлемента.Заголовок = СтроковыеФункцииКлиент.ФорматированнаяСтрока(ТекстПодсказки);
		
	КонецЕсли;
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ПредставлениеВладельца(ОсновныеМетаданные)
	Для каждого Реквизит Из ОсновныеМетаданные.СтандартныеРеквизиты Цикл
		Если Реквизит.Имя = "Владелец" Тогда
			Возврат Реквизит.Синоним;	
		КонецЕсли;
	КонецЦикла;
	Возврат "";	
КонецФункции

&НаКлиентеНаСервереБезКонтекста
Функция ЗначениеРеквизита(Знач Данные, Знач ИмяРеквизита, Знач ЗначениеПриОтсутствии = Неопределено)
	// Безопасное получение значения реквизита.
	Проба = Новый Структура(ИмяРеквизита);
	
	ЗаполнитьЗначенияСвойств(Проба, Данные);
	Если Проба[ИмяРеквизита] <> Неопределено Тогда
		// Есть значение
		Возврат Проба[ИмяРеквизита];
	КонецЕсли;
	
	// Возможно значение в данных равно Неопределено.
	Проба[ИмяРеквизита] = Истина;
	ЗаполнитьЗначенияСвойств(Проба, Данные);
	Если Проба[ИмяРеквизита] <> Истина Тогда
		Возврат Проба[ИмяРеквизита];
	КонецЕсли;
	
	Возврат ЗначениеПриОтсутствии;
КонецФункции

&НаСервере
Функция ПроверитьВозможностьЗаменыСсылок()
	
	ПарыЗамен = Новый Соответствие;
	Для Каждого Строка Из ЗаменяемыеСсылки Цикл
		ПарыЗамен.Вставить(Строка.Ссылка, ЦелевойЭлемент);
	КонецЦикла;
	
	ПараметрыЗамены = Новый Структура("СпособУдаления", ТекущийВариантУдаления);
	Возврат ПоискИУдалениеДублей.ПроверитьВозможностьЗаменыЭлементовСтрока(ПарыЗамен, ПараметрыЗамены);
	
КонецФункции

// Параметры:
//   Ссылки - СписокЗначений из ЛюбаяСсылка
//          - Массив из ЛюбаяСсылка
//          - ТаблицаЗначений:
//        * Ссылка - ЛюбаяСсылка
// Возвращаемое значение:
//   Массив
//
&НаСервереБезКонтекста
Функция МассивСсылокИзСписка(Знач Ссылки)
	// Преобразует массив, список значений или коллекцию в массив.
	
	ТипПараметра = ТипЗнч(Ссылки);
	Если Ссылки = Неопределено Тогда
		МассивСсылок = Новый Массив;
		
	ИначеЕсли ТипПараметра  = Тип("СписокЗначений") Тогда
		МассивСсылок = Ссылки.ВыгрузитьЗначения();
		
	ИначеЕсли ТипПараметра = Тип("Массив") Тогда
		МассивСсылок = Ссылки;
		
	Иначе
		МассивСсылок = Новый Массив;
		Для Каждого Элемент Из Ссылки Цикл
			МассивСсылок.Добавить(Элемент.Ссылка);
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат МассивСсылок;
КонецФункции

&НаСервереБезКонтекста
Функция КодОбъекта(Знач Ссылка, КэшМетаданных)
	
	Мета = Ссылка.Метаданные();
	ЕстьКод = КэшМетаданных[Мета];
	
	Если ЕстьКод = Неопределено Тогда
		// Проверяем, если ли код вообще.
		Тест = Новый Структура("ДлинаКода", 0);
		ЗаполнитьЗначенияСвойств(Тест, Мета);
		ЕстьКод = Тест.ДлинаКода > 0;
		
		КэшМетаданных[Мета] = ЕстьКод;
	КонецЕсли;
	
	Возврат ?(ЕстьКод, ОбщегоНазначения.ЗначениеРеквизитаОбъекта(Ссылка, "Код"), Неопределено);
КонецФункции

&НаСервере
Процедура ИнициализироватьЗаменяемыеСсылки(Знач МассивСсылок)
	
	КоличествоСсылок = МассивСсылок.Количество();
	Если КоличествоСсылок = 0 Тогда
		ВызватьИсключение НСтр("ru = 'Выберите хотя бы один элемент для замены.'");
	КонецЕсли;
	
	ЦелевойЭлемент = МассивСсылок[0];
	ОсновныеМетаданные = ЦелевойЭлемент.Метаданные();
	ВыполнитьПроверкуПравДоступа("Изменение", ОсновныеМетаданные);
	
	Характеристики = Новый Структура("Владельцы, Иерархический, ВидИерархии", Новый Массив, Ложь);
	ЗаполнитьЗначенияСвойств(Характеристики, ОсновныеМетаданные);
	
	ЕстьВладельцы = Характеристики.Владельцы.Количество() > 0;
	ЕстьГруппы    = Характеристики.Иерархический И Характеристики.ВидИерархии = Метаданные.СвойстваОбъектов.ВидИерархии.ИерархияГруппИЭлементов;
	
	ТекстЗапроса =
		"ВЫБРАТЬ
		|Ссылка КАК Ссылка,
		|&ПолеВладелец КАК Владелец,
		|&ЭтоГруппа КАК ЭтоГруппа
		|ПОМЕСТИТЬ ЗаменяемыеСсылки
		|ИЗ
		|	#ИмяТаблицы
		|ГДЕ
		|	Ссылка В (&НаборСсылок)
		|ИНДЕКСИРОВАТЬ ПО
		|	Владелец,
		|	ЭтоГруппа
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗРЕШЕННЫЕ
		|	КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Владелец) КАК КоличествоВладельцев,
		|	МИНИМУМ(Владелец)              КАК ОбщийВладелец,
		|	МАКСИМУМ(ЭтоГруппа)            КАК ЕстьГруппы,
		|	КОЛИЧЕСТВО(Ссылка)             КАК КоличествоСсылок
		|ИЗ
		|	ЗаменяемыеСсылки
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗРЕШЕННЫЕ ПЕРВЫЕ 1
		|	ЦелеваяТаблица.Ссылка
		|ИЗ
		|	#ИмяТаблицы КАК ЦелеваяТаблица
		|		ЛЕВОЕ СОЕДИНЕНИЕ ЗаменяемыеСсылки КАК ЗаменяемыеСсылки
		|		ПО ЦелеваяТаблица.Ссылка = ЗаменяемыеСсылки.Ссылка
		|ГДЕ
		|	ЗаменяемыеСсылки.Ссылка ЕСТЬ NULL
		|	И &УсловиеГруппа
		|	И &УсловиеВладелец";
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "#ИмяТаблицы", ОсновныеМетаданные.ПолноеИмя());
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ПолеВладелец", ?(ЕстьВладельцы, "Владелец", "НЕОПРЕДЕЛЕНО"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&ЭтоГруппа", ?(ЕстьГруппы, "ЭтоГруппа", "ЛОЖЬ"));
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеВладелец", 
		?(ЕстьВладельцы, "ЦелеваяТаблица.Владелец = &Владелец", "ИСТИНА")); // @query-part
	ТекстЗапроса = СтрЗаменить(ТекстЗапроса, "&УсловиеГруппа", 
		?(ЕстьГруппы, "НЕ ЦелеваяТаблица.ЭтоГруппа", "ИСТИНА")); // @query-part
		
	Запрос = Новый Запрос(ТекстЗапроса);
	Запрос.УстановитьПараметр("НаборСсылок", МассивСсылок);
	Если ЕстьВладельцы Тогда
		Запрос.УстановитьПараметр("Владелец", ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ЦелевойЭлемент, "Владелец"));
	КонецЕсли;
	
	Результат = Запрос.ВыполнитьПакет();
	Условия = Результат[1].Выгрузить()[0];
	Если Условия.ЕстьГруппы Тогда
		ВызватьИсключение НСтр("ru = 'Один из заменяемых элементов является группой.
			|Группы не могут быть заменены.'");
	ИначеЕсли Условия.КоличествоВладельцев > 1 Тогда 
		ВызватьИсключение НСтр("ru = 'У заменяемых элементов разные владельцы.
			|Такие элементы не могут быть заменены.'");
	ИначеЕсли Условия.КоличествоСсылок <> КоличествоСсылок Тогда
		ВызватьИсключение НСтр("ru = 'Все заменяемые элементы должны быть одного типа.'");
	КонецЕсли;
	
	Если Результат[2].Выгрузить().Количество() = 0 Тогда
		Если КоличествоСсылок > 1 Тогда
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Выбранные элементы (%1) не на что заменить, т.к. нет других подходящих элементов для замены.'"), 
				КоличествоСсылок);
		Иначе
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Выбранный элемент ""%1"" не на что заменить, т.к. нет других подходящих элементов для замены.'"), 
				ОбщегоНазначения.ПредметСтрокой(ЦелевойЭлемент));
		КонецЕсли;
	КонецЕсли;
	
	ОбщийВладелецЗаменяемыхСсылок = ?(ЕстьВладельцы, Условия.ОбщийВладелец, Неопределено);
	Для Каждого Элемент Из МассивСсылок Цикл
		ЗаменяемыеСсылки.Добавить().Ссылка = Элемент;
	КонецЦикла;
	Элементы.Список.Отображение = ?(Условия.ЕстьГруппы, ОтображениеТаблицы.ИерархическийСписок, ОтображениеТаблицы.Список);
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Работа с длительными операциями

&НаКлиенте
Процедура ЗапуститьФоновоеЗаданиеКлиент()
	
	ПараметрыМетода = Новый Структура("ПарыЗамен, СпособУдаления");
	ПараметрыМетода.ПарыЗамен = Новый Соответствие;
	Для Каждого Строка Из ЗаменяемыеСсылки Цикл
		ПараметрыМетода.ПарыЗамен.Вставить(Строка.Ссылка, ЦелевойЭлемент);
	КонецЦикла;
	ПараметрыМетода.Вставить("СпособУдаления", ТекущийВариантУдаления);
	
	Задание = ЗапуститьФоновоеЗадание(ПараметрыМетода, УникальныйИдентификатор);
	
	НастройкиОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
	НастройкиОжидания.ВыводитьОкноОжидания = Ложь;
	
	Обработчик = Новый ОписаниеОповещения("ПослеЗавершенияФоновогоЗадания", ЭтотОбъект);
	ДлительныеОперацииКлиент.ОжидатьЗавершение(Задание, Обработчик, НастройкиОжидания);
	
КонецПроцедуры

&НаСервереБезКонтекста
Функция ЗапуститьФоновоеЗадание(Знач ПараметрыМетода, Знач УникальныйИдентификатор)
	
	ИмяМетода = "ПоискИУдалениеДублей.ЗаменитьСсылки";
	НаименованиеМетода = НСтр("ru = 'Поиск и удаление дублей: Замена ссылок'");
	
	НастройкиЗапуска = ДлительныеОперации.ПараметрыВыполненияВФоне(УникальныйИдентификатор);
	НастройкиЗапуска.НаименованиеФоновогоЗадания = НаименованиеМетода;
	
	Возврат ДлительныеОперации.ВыполнитьВФоне(ИмяМетода, ПараметрыМетода, НастройкиЗапуска);
	
КонецФункции

&НаКлиенте
Процедура ПослеЗавершенияФоновогоЗадания(Задание, ДополнительныеПараметры) Экспорт
	
	НастройкиМастера.ПоказатьДиалогПередЗакрытием = Ложь;
	Если Задание = Неопределено 
		ИЛИ Элементы.ШагиМастера.ТекущаяСтраница = Элементы.ШагВыборЦелевогоЭлемента Тогда
		Возврат;
	КонецЕсли;
	
	Если Задание.Статус <> "Выполнено" Тогда
		// Фоновое задание завершено с ошибкой.
		Кратко = НСтр("ru = 'Не выполнена замена элементов по причине:'") + Символы.ПС + Задание.КраткоеПредставлениеОшибки;
		Подробно = Кратко + Символы.ПС + Символы.ПС + Задание.ПодробноеПредставлениеОшибки;
		Элементы.НадписьТекстОшибки.Заголовок = Кратко;
		Элементы.СсылкаПодробнее.Подсказка    = Подробно;
		ПерейтиНаШагМастера(Элементы.ШагВозниклаОшибка);
		Активизировать();
		Возврат;
	КонецЕсли;
	
	ЕстьНеудачныеЗамены = ЗаполнитьНеудачныеЗамены(Задание.АдресРезультата);
	Если ЕстьНеудачныеЗамены Тогда
		// Частично успешно - вывести расшифровку.
		ПерейтиНаШагМастера(Элементы.ШагПовторЗамены);
		Активизировать();
	Иначе
		// Полностью успешно - вывести оповещение и закрыть форму.
		Количество = ЗаменяемыеСсылки.Количество();
		Если Количество = 1 Тогда
			ТекстРезультата = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Элемент ""%1"" заменен на ""%2""'"),
				ЗаменяемыеСсылки[0].Ссылка,
				ЦелевойЭлементРезультат);
		Иначе
			ТекстРезультата = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Элементы (%1) заменены на ""%2""'"),
				Количество,
				ЦелевойЭлементРезультат);
		КонецЕсли;
		ПоказатьОповещениеПользователя(
			,
			ПолучитьНавигационнуюСсылку(ЦелевойЭлемент),
			ТекстРезультата,
			БиблиотекаКартинок.Информация32);
		СписокОбновленного = Новый Массив;
		Для Каждого Строка Из ЗаменяемыеСсылки Цикл
			СписокОбновленного.Добавить(Строка.Ссылка);
		КонецЦикла;
		ОповеститьОбУспешнойЗамене(СписокОбновленного);
		Закрыть();
	КонецЕсли
	
КонецПроцедуры

&НаСервере
Функция ЗаполнитьНеудачныеЗамены(Знач АдресРезультата)
	РезультатыЗамены = ПолучитьИзВременногоХранилища(АдресРезультата); // см. ОбщегоНазначения.ЗаменитьСсылки
	
	КорневыеСтроки = НеудачныеЗамены.ПолучитьЭлементы();
	КорневыеСтроки.Очистить();
	
	СоответствиеСтрок = Новый Соответствие;
	КэшМетаданных     = Новый Соответствие;
	
	Для Каждого СтрокаРезультата Из РезультатыЗамены Цикл
		Ссылка = СтрокаРезультата.Ссылка;
		
		ОшибкиПоСсылке = СоответствиеСтрок[Ссылка];
		Если ОшибкиПоСсылке = Неопределено Тогда
			СтрокаДерева = КорневыеСтроки.Добавить();
			СтрокаДерева.Ссылка = Ссылка;
			СтрокаДерева.Данные = Строка(Ссылка);
			СтрокаДерева.Код    = Строка(КодОбъекта(Ссылка, КэшМетаданных));
			СтрокаДерева.Пиктограмма = -1;
			
			ОшибкиПоСсылке = СтрокаДерева.ПолучитьЭлементы();
			СоответствиеСтрок.Вставить(Ссылка, ОшибкиПоСсылке);
		КонецЕсли;
		
		СтрокаОшибки = ОшибкиПоСсылке.Добавить();
		СтрокаОшибки.Ссылка = СтрокаРезультата.ОбъектОшибки;
		СтрокаОшибки.Данные = СтрокаРезультата.ПредставлениеОбъектаОшибки;
		
		ТипОшибки = СтрокаРезультата.ТипОшибки;
		Если ТипОшибки = "НеизвестныеДанные" Тогда
			СтрокаОшибки.Причина = НСтр("ru = 'Обнаружена данные, обработка которых не планировалась.'");
			
		ИначеЕсли ТипОшибки = "ОшибкаБлокировки" Тогда
			СтрокаОшибки.Причина = НСтр("ru = 'Не удалось заблокировать данные.'");
			
		ИначеЕсли ТипОшибки = "ДанныеИзменены" Тогда
			СтрокаОшибки.Причина = НСтр("ru = 'Данные изменены другим пользователем.'");
			
		ИначеЕсли ТипОшибки = "ОшибкаЗаписи" Тогда
			СтрокаОшибки.Причина = СтрокаРезультата.ТекстОшибки;
			
		ИначеЕсли ТипОшибки = "ОшибкаУдаления" Тогда
			СтрокаОшибки.Причина = НСтр("ru = 'Невозможно удалить данные.'");
			
		Иначе
			СтрокаОшибки.Причина = НСтр("ru = 'Непредвиденная ситуация.'");
			
		КонецЕсли;
		
		СтрокаОшибки.ПодробнаяПричина = СтрокаРезультата.ТекстОшибки;
	КонецЦикла;
	
	Возврат КорневыеСтроки.Количество() > 0;
КонецФункции

&НаКлиенте
Процедура ПослеПодтвержденияОтменыЗадания(Ответ, ПараметрыВыполнения) Экспорт
	Если Ответ = КодВозвратаДиалога.Прервать
		И Элементы.ШагиМастера.ТекущаяСтраница = Элементы.ШагЗамена Тогда
		НастройкиМастера.ПоказатьДиалогПередЗакрытием = Ложь;
		Закрыть();
	КонецЕсли;
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Служебные процедуры и функции мастера

// Описание настроек кнопки мастера.
//
// Возвращаемое значение:
//  Структура - настройки кнопки формы, где:
//    * Заголовок         - Строка - заголовок кнопки.
//    * Подсказка         - Строка - подсказка для кнопки.
//    * Видимость         - Булево - когда Истина то кнопка видна. Значение по умолчанию: Истина.
//    * Доступность       - Булево - когда Истина то кнопку можно нажимать. Значение по умолчанию: Истина.
//    * КнопкаПоУмолчанию - Булево - когда Истина то кнопка будет основной кнопкой формы. Значение по умолчанию - Ложь.
//    * РасширеннаяПодсказка - Структура:
//    ** Заголовок - Строка
//
&НаКлиентеНаСервереБезКонтекста
Функция КнопкаМастера()
	Результат = Новый Структура;
	Результат.Вставить("Заголовок", "");
	Результат.Вставить("Подсказка", "");
	
	Результат.Вставить("Доступность", Истина);
	Результат.Вставить("Видимость", Истина);
	Результат.Вставить("КнопкаПоУмолчанию", Ложь);
	
	Возврат Результат;
КонецФункции

// Параметры:
//  КнопкаМастера - см. КнопкаМастера
//  Описание - Строка
//
&НаКлиентеНаСервереБезКонтекста
Процедура ОбновитьСвойстваКнопкиМастера(КнопкаМастера, Описание)
	
	ЗаполнитьЗначенияСвойств(КнопкаМастера, Описание);
	КнопкаМастера.РасширеннаяПодсказка.Заголовок = Описание.Подсказка;
	
КонецПроцедуры

#КонецОбласти